#pragma once
#include <pthread.h>
#include <vector>
#include <semaphore.h>
using namespace std;


template<class T>
class RingQueue
{
public:
    RingQueue(int maxcap = 8)
        :_queue(maxcap)
        ,_maxcap(maxcap)
    {
        pthread_mutex_init(&_c_mutex, nullptr);
        pthread_mutex_init(&_p_mutex, nullptr);
        //pthread_cond_init(&_c_cond, nullptr);
        //pthread_cond_init(&_p_cond, nullptr);
        sem_init(&_data_sem, 0, 0);
        sem_init(&_room_sem, 0, maxcap);
    }


    void push(const T& val)
    {
        sem_wait(&_room_sem);
        pthread_mutex_lock(&_p_mutex);
        _queue[_p_pos] = val;
        _p_pos = (_p_pos + 1) % _maxcap;
        pthread_mutex_unlock(&_p_mutex);
        sem_post(&_data_sem);
    }


    T pop()
    {
        sem_wait(&_data_sem);
        pthread_mutex_lock(&_c_mutex);
        T ret = _queue[_c_pos];
        _c_pos = (_c_pos + 1) % _maxcap;
        pthread_mutex_unlock(&_c_mutex);
        sem_post(&_room_sem);
        return ret;
    }


    ~RingQueue()
    {
        pthread_mutex_destroy(&_c_mutex);
        pthread_mutex_destroy(&_p_mutex);
        //pthread_cond_destroy(&_c_cond);
        //pthread_cond_destroy(&_p_cond);
        sem_destroy(&_data_sem);
        sem_destroy(&_room_sem);
    }
    
private:
    vector<T> _queue;

    int _maxcap;

    int _c_pos = 0;
    int _p_pos = 0;

    pthread_mutex_t _c_mutex;
    pthread_mutex_t _p_mutex;
    //pthread_cond_t _c_cond;
    //pthread_cond_t _p_cond;
    sem_t _data_sem;
    sem_t _room_sem;
};